package kb;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import utils.Display;

import entities.Individual;
import entities.assertion.Assertion;
import entities.assertion.ConceptAssertion;
import entities.assertion.RoleAssertion;
import entities.concept.AndConcept;
import entities.concept.Concept;
import entities.role.Role;

public final class ABox {

	private HashMap<Individual, Concept> indexConceptAssertions;
	private Map<Individual, HashMap<Role, HashSet<Individual>>> indexRoleAssertions;
	private Set<Individual> allABoxIndividuals;

	public void cleanUp() {
		// clean all individuals in this abox, as they may have been changed in
		// previous reasoning sessions
		for (Individual ind : this.allABoxIndividuals) {
			ind.cleanUp();
		}
	}

	// //for every R(a,b), add R-(b,a)
	// public void ensureCompleteReasoning(){
	// Set<Assertion> ras = new HashSet<Assertion>();
	// for(Individual a:this.indexRoleAssertions.keySet()){
	// for(Role R: this.indexRoleAssertions.get(a).keySet()){
	// for(Individual b:this.indexRoleAssertions.get(a).get(R)){
	// Role ri = Role.getInverseRole(R);
	// ras.add(new RoleAssertion(b, a, ri));
	// }
	// }
	// }
	// this.insertAssertions(ras);
	// }

	// private void insertIndexedRA(Individual a, Role r, Individual b){
	// if(this.indexRoleAssertions.containsKey(a)){
	// if(this.indexRoleAssertions.get(a).containsKey(r)){
	// this.indexRoleAssertions.get(a).get(r).add(b);
	// }else{
	// HashSet<Individual> value = new HashSet<Individual>();
	// value.add(b);
	// this.indexRoleAssertions.get(a).put(r, value );
	// }
	// }
	// HashSet<Individual> value = new HashSet<Individual>();
	// value.add(b);
	// HashMap<Role, HashSet<Individual>> im = new HashMap<Role,
	// HashSet<Individual>>();
	// im.put(r, value);
	// this.indexRoleAssertions.put(a, im);
	// }

	public void addIndexedRoleAst(
			Map<Individual, HashMap<Role, HashSet<Individual>>> ras) {
		this.indexRoleAssertions.putAll(ras);
	}

	public ABox deepcopyOfThisABox() {
		ABox ab = new ABox();
		ab.allABoxIndividuals.addAll(this.allABoxIndividuals);
		ab.indexConceptAssertions.putAll(this.indexConceptAssertions);
		ab.indexRoleAssertions.putAll(this.indexRoleAssertions);
		return ab;
	}

	/*
	 * Given *:C for an abox, the abox populates this star assertion and return
	 * all reified assertions.
	 */
	public List<ConceptAssertion> populateStarConceptAssertion(
			ConceptAssertion spec) {
		List<ConceptAssertion> res = new LinkedList<ConceptAssertion>();
		Concept c = spec.getConcept();
		for (Individual ind : this.getABoxIndividuals()) {
			res.add(new ConceptAssertion(ind, c));
		}
		return res;
	}

	// add a file of assertions to an existing ABox
	// public static ABox addToABox(ABox ab, String file){
	// HashSet<Assertion> toadd = null;
	// try {
	// toadd = KBAccess.processAssertionFile(new BufferedReader(new
	// FileReader(file)), false);
	// } catch (FileNotFoundException e) {
	// e.printStackTrace();
	// } catch (IOException e) {
	// e.printStackTrace();
	// }
	// //index all assertions here
	// ab.insertAssertions(toadd);
	// return ab;
	// }

	public HashMap<Individual, Concept> getConceptAssertions() {
		return this.indexConceptAssertions;
	}

	public Map<Individual, HashMap<Role, HashSet<Individual>>> getRoleAssertions() {
		return this.indexRoleAssertions;
	}

	// public HashMap<String, Individual> getAllNamedIndividuals(){
	// return allIndividuals;
	// }

	// get data
	public Concept getConcept(Individual a) {
		return this.indexConceptAssertions.get(a);
	}

	public Set<Individual> getABoxIndividuals() {
		// System.err.println("instances from ABox: "+this.allABoxIndividuals.size());
		return new HashSet<Individual>(this.allABoxIndividuals);
	}

	public List<Individual> getListOfAllIndividuals() {
		return new ArrayList<Individual>(this.allABoxIndividuals);
	}

	public ABox() {
		this.indexConceptAssertions = new HashMap<Individual, Concept>();
		this.indexRoleAssertions = new HashMap<Individual, HashMap<Role, HashSet<Individual>>>();
		this.allABoxIndividuals = new HashSet<Individual>();
	}

	public boolean isRoleAssertionFreeABox() {
		return this.indexRoleAssertions.size() < 1;
	}

	// public void insertIndividuals(Individual ind){
	// allIndividuals.put(ind.getName(), ind);
	// }

	public void insertConceptAssertions(ConceptAssertion ca) {
		if (ca == null) {
			System.err.println("Null concept assertions given in "
					+ this.getClass().getName());
			return;
		}
		Individual ind = ca.getIndividual();
		if (ind.isVisible()) {
			this.allABoxIndividuals.add(ind);
		}
		// this.insertIndividuals(ind);
		Concept c = ca.getConcept();
		if (c.isTop()) {
			// a:T, no need to add for individual a.
			return;
		}
		if (this.indexConceptAssertions.containsKey(ind)) {
			c = new AndConcept(c, this.indexConceptAssertions.get(ind));
		}
		this.indexConceptAssertions.put(ind, c);
	}

	public void rmConceptAssertions(ConceptAssertion ca) {
		if (ca == null) {
			System.err.println("Null concept assertions given in "
					+ this.getClass().getName());
			return;
		}
		Individual ind = ca.getIndividual();
		// this.allABoxIndividuals.remove(ind);
		if (this.indexConceptAssertions.containsKey(ind)) {
			Concept ic = this.indexConceptAssertions.get(ind);
			if (ic instanceof AndConcept) {
				((AndConcept) ic).getConceptSet().remove(ca.getConcept());
			}
		}
	}

	public void insertAssertions(Set<Assertion> ax) {
		if (ax != null) {
			for (Assertion as : ax) {
				insertAssertions(as);
			}
		}
	}

	public void insertAssertions(Assertion ast) {
		if (ast == null) {
			return;
		}
		if (ast instanceof ConceptAssertion) {
			this.insertConceptAssertions((ConceptAssertion) ast);
		} else if (ast instanceof RoleAssertion) {
			this.insertRoleAssertions((RoleAssertion) ast);
		} else {
			System.err.println("Assertion type unknown: " + ast);
		}
	}

	public void rmAssertions(Assertion ast) {
		if (ast == null) {
			return;
		}
		if (ast instanceof ConceptAssertion) {
			this.rmConceptAssertions((ConceptAssertion) ast);
		} else if (ast instanceof RoleAssertion) {
			this.rmRoleAssertions((RoleAssertion) ast);
		} else {
			System.err.println("Assertion type unknown: " + ast);
		}
	}

	private void insertRoleAssertions(RoleAssertion ra) {
		Individual a = ra.getFirstIndividual();
		Individual b = ra.getSecondIndividual();
		if (a.isVisible()) {
			this.allABoxIndividuals.add(a);
		}
		if (b.isVisible()) {
			this.allABoxIndividuals.add(b);
		}
		Role r = ra.getRole();
		if (this.indexRoleAssertions.containsKey(a)) {
			if (this.indexRoleAssertions.get(a).containsKey(r)) {
				this.indexRoleAssertions.get(a).get(r).add(b);
			} else {
				// no such r.b
				HashSet<Individual> bs = new HashSet<Individual>();
				bs.add(b);
				this.indexRoleAssertions.get(a).put(r, bs);
			}
		} else {
			// no such a.r.b
			HashMap<Role, HashSet<Individual>> inner = new HashMap<Role, HashSet<Individual>>();
			HashSet<Individual> bs = new HashSet<Individual>();
			bs.add(b);
			inner.put(r, bs);
			this.indexRoleAssertions.put(a, inner);
		}
		return;
	}

	public void rmRoleAssertions(RoleAssertion ra) {
		Individual a = ra.getFirstIndividual();
		Individual b = ra.getSecondIndividual();
		// this.allABoxIndividuals.remove(a);
		// this.allABoxIndividuals.remove(b);
		Role r = ra.getRole();
		if (this.indexRoleAssertions.containsKey(a)) {
			if (this.indexRoleAssertions.get(a).containsKey(r)) {
				this.indexRoleAssertions.get(a).get(r).remove(b);
				if (this.indexRoleAssertions.get(a).get(r).size() < 1) {
					this.indexRoleAssertions.get(a).remove(r);
					if (this.indexRoleAssertions.get(a).size() < 1) {
						this.indexRoleAssertions.remove(a);
					}
				}
			}
		}
		return;
	}

	/**
	 * replicate this ABox using a string, which can be physically saved.
	 * 
	 * @param sb
	 */
	public StringBuilder save(StringBuilder sb) {
		sb.append("\n###### ABox Assertions ######\n");
		// save concept assertions
		for (Individual n : this.indexConceptAssertions.keySet()) {
			Concept c = this.indexConceptAssertions.get(n);
			sb.append("(concept-assertion ");
			sb.append(n.toString());
			sb.append(" ");
			sb.append(c.toSerializedString());
			sb.append(")\n");
		}
		for (Individual a : this.indexRoleAssertions.keySet()) {
			Map<Role, HashSet<Individual>> rs = this.indexRoleAssertions.get(a);
			for (Role r : rs.keySet()) {
				for (Individual b : rs.get(r)) {
					sb.append("(role-assertion ");
					sb.append(a.toString());
					sb.append(" ");
					sb.append(b.toString());
					sb.append(" ");
					sb.append(r.toSerializedString());
					sb.append(")\n");
				}
			}

		}
		return sb;
	}

	@Override
	public String toString() {
		String ret = "";
		ret += "\n============\nAssertions in the ABox " + Display.ABOXUNICODE
				+ ":\n============\n";
		ret += "\n============Concept Assertions============\n";
		for (Entry<Individual, Concept> e : this.indexConceptAssertions
				.entrySet()) {
			ret += e.getKey().toString() + " : " + e.getValue().toString()
					+ "\n";
		}
		ret += "\n============Role Assertions============\n";
		for (Entry<Individual, HashMap<Role, HashSet<Individual>>> e : this.indexRoleAssertions
				.entrySet()) {
			for (Entry<Role, HashSet<Individual>> ee : e.getValue().entrySet()) {
				for (Individual ind : ee.getValue()) {
					ret += "(" + e.getKey().toString() + ", " + ind.toString()
							+ ") : " + ee.getKey().toString() + "\n";
				}
			}
		}
		return ret;
	}

}
